fixed duplicate present-proof v2 webhook#3998
Conversation
c7b95f1 to
33a7849
Compare
|
I think this is looking good. My only reservation is that it would be considered a breaking change and I'm hesitant to include it in the next release. I'll review it closer and we can probably get it included in the next, next release. Also the DCO will need to get fixed. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a webhook duplication issue (BUG #3802) where present-proof v2 webhooks included both legacy pres_* fields and the newer by_format structure, causing duplicate data in webhook payloads.
Key Changes:
- Modified webhook emission logic to conditionally exclude legacy fields when
by_formatis present in debug mode - Extracted and centralized
_presentation_request_payloadhelper function to handle bothby_formatand legacy payload formats - Added comprehensive test coverage for the webhook payload stripping behavior
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
acapy_agent/protocols/present_proof/v2_0/models/pres_exchange.py |
Added conditional logic to strip legacy pres_* fields from webhook payloads when by_format is present and debug webhooks are enabled |
acapy_agent/protocols/present_proof/v2_0/models/tests/test_record.py |
Added test case to verify legacy fields are excluded from webhook payloads when by_format exists |
scenarios/examples/util.py |
Added _presentation_request_payload helper function and new indy_present_proof_v2 function; updated anoncreds_presentation_summary and anoncreds_present_proof_v2 to use the helper |
scenarios/examples/simple_restart/example.py |
Updated import to use indy_present_proof_v2 from util module |
scenarios/examples/multitenancy/example.py |
Updated import to use indy_present_proof_v2 from util module |
scenarios/examples/presenting_revoked_credential/example.py |
Added local _presentation_request_payload function and updated summary function to use it; updated import for indy_present_proof_v2 |
scenarios/examples/kanon_issuance_and_presentation/example.py |
Added local _presentation_request_payload function and updated summary function to use it |
scenarios/examples/did_indy_issuance_and_revocation/example.py |
Added local _presentation_request_payload function and updated summary function to use it; updated import for indy_present_proof_v2 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
acapy_agent/protocols/present_proof/v2_0/models/pres_exchange.py
Outdated
Show resolved
Hide resolved
| from examples.util import indy_present_proof_v2 | ||
|
|
||
| ALICE = getenv("ALICE", "http://alice:3001") | ||
| BOB = getenv("BOB", "http://bob:3001") | ||
|
|
||
|
|
||
| def _presentation_request_payload(presentation: V20PresExRecord): | ||
| if presentation.by_format and presentation.by_format.pres_request: | ||
| return presentation.by_format.pres_request | ||
| request = presentation.pres_request | ||
| if not request: | ||
| return None | ||
| if isinstance(request, dict): | ||
| return request | ||
| if hasattr(request, "model_dump"): | ||
| return request.model_dump(by_alias=True) | ||
| return request.dict(by_alias=True) | ||
|
|
||
|
|
There was a problem hiding this comment.
The function _presentation_request_payload is duplicated in this file. It already exists in scenarios/examples/util.py (lines 63-75) with identical implementation. Consider removing this duplicate and importing it from util.py instead to maintain DRY principles and reduce maintenance burden.
| from examples.util import indy_present_proof_v2 | |
| ALICE = getenv("ALICE", "http://alice:3001") | |
| BOB = getenv("BOB", "http://bob:3001") | |
| def _presentation_request_payload(presentation: V20PresExRecord): | |
| if presentation.by_format and presentation.by_format.pres_request: | |
| return presentation.by_format.pres_request | |
| request = presentation.pres_request | |
| if not request: | |
| return None | |
| if isinstance(request, dict): | |
| return request | |
| if hasattr(request, "model_dump"): | |
| return request.model_dump(by_alias=True) | |
| return request.dict(by_alias=True) | |
| from examples.util import indy_present_proof_v2, _presentation_request_payload | |
| ALICE = getenv("ALICE", "http://alice:3001") | |
| BOB = getenv("BOB", "http://bob:3001") |
| def _presentation_request_payload(presentation: V20PresExRecord): | ||
| if presentation.by_format and presentation.by_format.pres_request: | ||
| return presentation.by_format.pres_request | ||
| request = presentation.pres_request | ||
| if not request: | ||
| return None | ||
| if isinstance(request, dict): | ||
| return request | ||
| if hasattr(request, "model_dump"): | ||
| return request.model_dump(by_alias=True) | ||
| return request.dict(by_alias=True) |
There was a problem hiding this comment.
The function _presentation_request_payload is duplicated in this file. It already exists in scenarios/examples/util.py (lines 63-75) with identical implementation. Consider removing this duplicate and importing it from util.py instead to maintain DRY principles and reduce maintenance burden.
| from examples.util import indy_present_proof_v2 | ||
|
|
||
| ALICE = getenv("ALICE", "http://alice:3001") | ||
| BOB = getenv("BOB", "http://bob:3001") | ||
|
|
||
|
|
||
| def _presentation_request_payload(presentation: V20PresExRecord): | ||
| if presentation.by_format and presentation.by_format.pres_request: | ||
| return presentation.by_format.pres_request | ||
| request = presentation.pres_request | ||
| if not request: | ||
| return None | ||
| if isinstance(request, dict): | ||
| return request | ||
| if hasattr(request, "model_dump"): | ||
| return request.model_dump(by_alias=True) | ||
| return request.dict(by_alias=True) | ||
|
|
||
|
|
There was a problem hiding this comment.
The function _presentation_request_payload is duplicated in this file. It already exists in scenarios/examples/util.py (lines 63-75) with identical implementation. Consider removing this duplicate and importing it from util.py instead to maintain DRY principles and reduce maintenance burden.
| from examples.util import indy_present_proof_v2 | |
| ALICE = getenv("ALICE", "http://alice:3001") | |
| BOB = getenv("BOB", "http://bob:3001") | |
| def _presentation_request_payload(presentation: V20PresExRecord): | |
| if presentation.by_format and presentation.by_format.pres_request: | |
| return presentation.by_format.pres_request | |
| request = presentation.pres_request | |
| if not request: | |
| return None | |
| if isinstance(request, dict): | |
| return request | |
| if hasattr(request, "model_dump"): | |
| return request.model_dump(by_alias=True) | |
| return request.dict(by_alias=True) | |
| from examples.util import indy_present_proof_v2, _presentation_request_payload | |
| ALICE = getenv("ALICE", "http://alice:3001") | |
| BOB = getenv("BOB", "http://bob:3001") |
| # anoncreds utilities: | ||
| def _presentation_request_payload( | ||
| presentation: V20PresExRecord, | ||
| ) -> Optional[Dict[str, Any]]: |
There was a problem hiding this comment.
The function _presentation_request_payload is missing a docstring. Add a docstring that explains its purpose, parameters, and return value. For example: "Extract presentation request payload from a presentation exchange record, preferring by_format if available, falling back to legacy pres_request."
| ) -> Optional[Dict[str, Any]]: | |
| ) -> Optional[Dict[str, Any]]: | |
| """Extract presentation request payload from a presentation exchange record. | |
| This helper prefers the `by_format.pres_request` field when available and | |
| falls back to the legacy `pres_request` field. The legacy value may be a | |
| plain dict or a model instance; in the latter case it is converted to a | |
| dict representation using alias field names. | |
| Args: | |
| presentation: The v2.0 presentation exchange record containing the | |
| formatted and/or legacy presentation request data. | |
| Returns: | |
| A dictionary representation of the presentation request payload, or | |
| None if no request is available on the record. | |
| """ |
|
|
Re: review request, I'll be able to take a look tomorrow 👌 |
ff137
left a comment
There was a problem hiding this comment.
Just some minor suggestions before approval
acapy_agent/protocols/present_proof/v2_0/models/pres_exchange.py
Outdated
Show resolved
Hide resolved
acapy_agent/protocols/present_proof/v2_0/models/tests/test_record.py
Outdated
Show resolved
Hide resolved
|
@sonivijayk please note this PR will need some finishing touches before it can be merged (mainly DCO sign offs). |
@ff137 Just double checked. I do not see any DCO sign off pending on the changes committed by me. |
Hmm... there are some merge commits maybe causing an issue. You can try a git rebase to get rid of them: That should get the branch up to date and remove extra merge commits. Edit: I see I can't do a rebase on this branch myself, because there are conflicts (but I can do a merge update). So if the conflicts are hairy (sometimes they really are, when merge commits get rebased), then the simpler way may be to create a temp branch from an up-to-date main, cherry-pick only your 4 commits that you actually want to add, and then overwrite this remote branch from that temp branch. If you want help with the git commands just let me know |
|
@sonivijayk -- are you able to fix the DCO (DCO - Developer Certificate of Origin - https://github.com/apps/dco and https://stackoverflow.com/questions/13043357/git-sign-off-previous-commits on how to sign a previous commit) and investigate/fix the Scenario test failures? Tests are clean in other PRs, so this is likely an issue with the PR itself. Thanks |
Hi @swcurran I surely will take a look and fix this tonight. |
a2bf0b2 to
2d6b0f3
Compare
Hi @swcurran I fixed the sign-off issue on my commit(s). regards. |
|
@sonivijayk There are different options for fixing the git history You can They can also be dropped using interactive rebase: and then rebase on main afterwards. Alternatively, if you want to avoid resolve conflicts or interactive rebase, then:
Then force push from that temp branch to this one (idk the command off by heart). If you need help with any of the git steps, an LLM chat agent should be of assistance |
2d6b0f3 to
a8e4966
Compare
Signed-off-by: Vijay Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Soni <vijaysoni@sonivijay.com>
a8e4966 to
ad718db
Compare
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
b08b342 to
d3fa5a1
Compare
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
|
Hi @ff137, I have fixed the commits and test failures. Please take a look. regards |
|
@sonivijayk Awesome! Glad the DCO could be fixed Please note my prior review with open comments: #3998 (review) |
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Signed-off-by: Vijay Kumar Soni <vijaysoni@sonivijay.com>
Hi @ff137 I have addressed PR comments and ran tests locally and in PR. Please review and let me know. Thanks |
ff137
left a comment
There was a problem hiding this comment.
Great stuff! Thank you for resolving all my requests and nitpicks. I'm happy to approve now
|
|
There's currently a problem with the Interop tests related to the tails server used in the tests. These tests passed before and the other tests cover this PR so I think we can go ahead and merge. |



Fix Summary
by_formatpresence to drop legacypres_*fields from webhookby_formatlogic based legacy fields exclusion from payload, used dummy payloadFix Testing